﻿using System;
using System.Text;
using System.Collections.Generic;
using System.Linq;
using Microsoft.VisualStudio.TestTools.UnitTesting;

namespace MethodWorx.CMS.TemplateParser.Tests
{
    [TestClass]
    public class XmlParserTests
    {
        [TestMethod]
        public void ParseOnlyText()
        {
            XmlParser parser = new XmlParser("This is text");
            

            Assert.IsNotNull(parser.Document);
            AssertDocumentAsNoErrors(parser.Document);
            Assert.AreEqual(1, parser.Document.Children.Count());
            Assert.IsTrue(parser.Document.Children.First() is TemplateText);
            Assert.AreEqual("This is text", ((TemplateText)parser.Document.Children.First()).Text);
            Assert.AreEqual(false, ((TemplateText)parser.Document.Children.First()).IsComment);
        }

        [TestMethod]
        public void ParseOnlyComment()
        {
            XmlParser parser = new XmlParser("<!-- This is a comment -->");


            Assert.IsNotNull(parser.Document);
            AssertDocumentAsNoErrors(parser.Document);
            Assert.AreEqual(1, parser.Document.Children.Count());
            Assert.IsTrue(parser.Document.Children.First() is TemplateText);
            Assert.AreEqual("<!-- This is a comment -->", ((TemplateText)parser.Document.Children.First()).Text);
            Assert.AreEqual(true, ((TemplateText)parser.Document.Children.First()).IsComment);
        }

        [TestMethod]
        public void ParseOnlyTextCheckBuffer()
        {
            XmlParser parser = new XmlParser("This is text");


            Assert.IsNotNull(parser.Document);
            AssertDocumentAsNoErrors(parser.Document);
            var element = parser.Document.AssertNodeMatches<TemplateText>(0, t => true);
            Assert.AreEqual("This is text", element.ParsedBuffer);
            Assert.AreEqual(0, element.TextStartingPosition);
        }

        [TestMethod]
        public void ParseElementOnly()
        {
            XmlParser parser = new XmlParser("<element></element>");

            Assert.IsNotNull(parser.Document);
            AssertDocumentAsNoErrors(parser.Document);
            //  should be two element, text and element
            Assert.AreEqual(1, parser.Document.Children.Count());
            //  first should be element
            Assert.IsTrue(parser.Document.Children.First() is TemplateElement);
            Assert.AreEqual("element", ((TemplateElement)parser.Document.Children.First()).Name);
        }

        [TestMethod]
        public void ParseElementOnlyCheckBuffer()
        {
            XmlParser parser = new XmlParser("<element></element>");

            Assert.IsNotNull(parser.Document);
            AssertDocumentAsNoErrors(parser.Document);
            var element = parser.Document.AssertNodeMatches<TemplateElement>(0, t => true);
            Assert.AreEqual("<element></element>", element.ParsedBuffer);
            Assert.AreEqual(1, element.ElementNameStringPosition);
            Assert.AreEqual(11, element.ElementEndValueStringPosition);

        }

        [TestMethod]
        public void ParseElementAndText()
        {
            XmlParser parser = new XmlParser("Some Text<element></element>");

            Assert.IsNotNull(parser.Document);
            AssertDocumentAsNoErrors(parser.Document);
            //  should be two element, text and element
            Assert.AreEqual(2, parser.Document.Children.Count());

            parser.Document.AssertNodeMatches<TemplateText>(0, t => t.Text == "Some Text");
            parser.Document.AssertNodeMatches<TemplateElement>(1, t => t.Name == "element");
        }

        [TestMethod]
        public void ParseTextAndElementAndMoreText()
        {
            XmlParser parser = new XmlParser("Some Text<element></element>Some More Text");

            Assert.IsNotNull(parser.Document);
            AssertDocumentAsNoErrors(parser.Document);
            //  should be two element, text and element
            Assert.AreEqual(3, parser.Document.Children.Count());

            parser.Document.AssertNodeMatches<TemplateText>(0, t => t.Text == "Some Text");
            parser.Document.AssertNodeMatches<TemplateElement>(1, t => t.Name == "element");
            parser.Document.AssertNodeMatches<TemplateText>(2, t => t.Text == "Some More Text");
        }

        [TestMethod]
        public void ParseElementAndAttribute()
        {
            XmlParser parser = new XmlParser("<e a=\"b\"></e>");

            Assert.IsNotNull(parser.Document);
            AssertDocumentAsNoErrors(parser.Document);
            //  should be two element, text and element
            Assert.AreEqual(1, parser.Document.Children.Count());

            var e = parser.Document.AssertNodeMatches<TemplateElement>(0, t => t.Name == "e");
            e.AssertNodeMatches<TemplateAttribute>(0, t => t.Name == "a" && t.Value == "b");
        }

        [TestMethod]
        public void ParseElementAndMultipleAttribute()
        {
            XmlParser parser = new XmlParser("<e a=\"b\" b=\"c\"></e>");

            Assert.IsNotNull(parser.Document);
            AssertDocumentAsNoErrors(parser.Document);
            //  should be two element, text and element
            Assert.AreEqual(1, parser.Document.Children.Count());

            var e = parser.Document.AssertNodeMatches<TemplateElement>(0, t => t.Name == "e");
            e.AssertNodeMatches<TemplateAttribute>(0, t => t.Name == "a" && t.Value == "b");
            e.AssertNodeMatches<TemplateAttribute>(1, t => t.Name == "b" && t.Value == "c");
        }

        [TestMethod]
        public void ParseElementAndMultipleAttributesWithoutQuotes()
        {
            XmlParser parser = new XmlParser("<e a=b b=c></e>");

            Assert.IsNotNull(parser.Document);
            AssertDocumentAsNoErrors(parser.Document);
            //  should be two element, text and element
            Assert.AreEqual(1, parser.Document.Children.Count());

            var e = parser.Document.AssertNodeMatches<TemplateElement>(0, t => t.Name == "e");
            e.AssertNodeMatches<TemplateAttribute>(0, t => t.Name == "a" && t.Value == "b");
            e.AssertNodeMatches<TemplateAttribute>(1, t => t.Name == "b" && t.Value == "c");
        }

        [TestMethod]
        public void ParseElementAndMultipleAttributesWithAndWithoutQuotes()
        {
            XmlParser parser = new XmlParser("<e a=b b=c c=\"d\"></e>");

            Assert.IsNotNull(parser.Document);
            AssertDocumentAsNoErrors(parser.Document);
            //  should be two element, text and element
            Assert.AreEqual(1, parser.Document.Children.Count());

            var e = parser.Document.AssertNodeMatches<TemplateElement>(0, t => t.Name == "e");
            e.AssertNodeMatches<TemplateAttribute>(0, t => t.Name == "a" && t.Value == "b");
            e.AssertNodeMatches<TemplateAttribute>(1, t => t.Name == "b" && t.Value == "c");
            e.AssertNodeMatches<TemplateAttribute>(2, t => t.Name == "c" && t.Value == "d");
        }

        [TestMethod]
        public void ParseElementAndMultipleAttributesWithoutQuotesAndWithoutSpaces()
        {
            XmlParser parser = new XmlParser("<e a=\"b\"b=c></e>");

            Assert.IsNotNull(parser.Document);
            AssertDocumentAsNoErrors(parser.Document);
            //  should be two element, text and element
            Assert.AreEqual(1, parser.Document.Children.Count());

            var e = parser.Document.AssertNodeMatches<TemplateElement>(0, t => t.Name == "e");
            e.AssertNodeMatches<TemplateAttribute>(0, t => t.Name == "a" && t.Value == "b");
            e.AssertNodeMatches<TemplateAttribute>(1, t => t.Name == "b" && t.Value == "c");
        }

        [TestMethod]
        public void ParseElementAndMultipleAttributesWithQuotesAndWithoutSpaces()
        {
            XmlParser parser = new XmlParser("<e a=\"b\"b=\"c\"></e>");

            Assert.IsNotNull(parser.Document);
            AssertDocumentAsNoErrors(parser.Document);
            //  should be two element, text and element
            Assert.AreEqual(1, parser.Document.Children.Count());

            var e = parser.Document.AssertNodeMatches<TemplateElement>(0, t => t.Name == "e");
            e.AssertNodeMatches<TemplateAttribute>(0, t => t.Name == "a" && t.Value == "b");
            e.AssertNodeMatches<TemplateAttribute>(1, t => t.Name == "b" && t.Value == "c");
        }


        [TestMethod]
        public void ParseElementAndMultipleAttributesWithQuotesInData()
        {
            XmlParser parser = new XmlParser("<e a=bab\"c\"></e>");

            Assert.IsNotNull(parser.Document);
            AssertDocumentAsNoErrors(parser.Document);
            //  should be two element, text and element
            Assert.AreEqual(1, parser.Document.Children.Count());

            var e = parser.Document.AssertNodeMatches<TemplateElement>(0, t => t.Name == "e");
            e.AssertNodeMatches<TemplateAttribute>(0, t => t.Name == "a" && t.Value == "bab\"c\"");
        }


        [TestMethod]
        public void ParseElementAndMultipleAttributesWithEqualsInData()
        {
            XmlParser parser = new XmlParser("<e a=ba=\"c\"></e>");

            Assert.IsNotNull(parser.Document);
            AssertDocumentAsNoErrors(parser.Document);
            //  should be two element, text and element
            Assert.AreEqual(1, parser.Document.Children.Count());

            var e = parser.Document.AssertNodeMatches<TemplateElement>(0, t => t.Name == "e");
            e.AssertNodeMatches<TemplateAttribute>(0, t => t.Name == "a" && t.Value == "ba=\"c\"");
        }

        [TestMethod]
        public void ParseElementAndMultipleAttributesWithEndElementInData()
        {
            XmlParser parser = new XmlParser("<e a=ba>\"c\"></e>");

            Assert.IsNotNull(parser.Document);
            AssertDocumentAsNoErrors(parser.Document);
            //  should be two element, text and element
            Assert.AreEqual(1, parser.Document.Children.Count());

            var e = parser.Document.AssertNodeMatches<TemplateElement>(0, t => t.Name == "e");
            e.AssertNodeMatches<TemplateAttribute>(0, t => t.Name == "a" && t.Value == "ba");
            e.AssertNodeMatches<TemplateText>(1, t => t.Text == "\"c\">");
        }

        [TestMethod]
        public void ParseElementAndChildElements()
        {
            XmlParser parser = new XmlParser("<e><ce></ce></e>");

            Assert.IsNotNull(parser.Document);
            AssertDocumentAsNoErrors(parser.Document);
            //  should be two element, text and element
            Assert.AreEqual(1, parser.Document.Children.Count());

            var e = parser.Document.AssertNodeMatches<TemplateElement>(0, t => t.Name == "e");
            e.AssertNodeMatches<TemplateElement>(0, t => t.Name == "ce");
        }

        [TestMethod]
        public void ParseElementAndChildElementsAndAttributes()
        {
            XmlParser parser = new XmlParser("<e><ce a=\"b\"></ce></e>");

            Assert.IsNotNull(parser.Document);
            AssertDocumentAsNoErrors(parser.Document);
            //  should be two element, text and element
            Assert.AreEqual(1, parser.Document.Children.Count());

            var e = parser.Document.AssertNodeMatches<TemplateElement>(0, t => t.Name == "e");
            var ce = e.AssertNodeMatches<TemplateElement>(0, t => t.Name == "ce");
            ce.AssertNodeMatches<TemplateAttribute>(0, t => t.Name == "a" && t.Value == "b");
        }


        [TestMethod]
        public void ParseElementAndChildElementsAndAttributesAndText()
        {
            XmlParser parser = new XmlParser("<e><ce a=\"b\">Some Text</ce></e>");

            Assert.IsNotNull(parser.Document);
            AssertDocumentAsNoErrors(parser.Document);
            //  should be two element, text and element
            Assert.AreEqual(1, parser.Document.Children.Count());

            var e = parser.Document.AssertNodeMatches<TemplateElement>(0, t => t.Name == "e");
            var ce = e.AssertNodeMatches<TemplateElement>(0, t => t.Name == "ce");
            ce.AssertNodeMatches<TemplateAttribute>(0, t => t.Name == "a" && t.Value == "b");
            ce.AssertNodeMatches<TemplateText>(1, t => t.Text == "Some Text");
        }


        [TestMethod]
        public void ParseElementAndMultipleChildElementsAndAttributesAndText()
        {
            XmlParser parser = new XmlParser("<e><ce a=\"b\">Some<mce>More</mce>Text</ce></e>");

            Assert.IsNotNull(parser.Document);
            AssertDocumentAsNoErrors(parser.Document);
            //  should be two element, text and element
            Assert.AreEqual(1, parser.Document.Children.Count());

            var e = parser.Document.AssertNodeMatches<TemplateElement>(0, t => t.Name == "e");
            var ce = e.AssertNodeMatches<TemplateElement>(0, t => t.Name == "ce");
            ce.AssertNodeMatches<TemplateAttribute>(0, t => t.Name == "a" && t.Value == "b");
            ce.AssertNodeMatches<TemplateText>(1, t => t.Text == "Some");
            ce.AssertNodeMatches<TemplateText>(3, t => t.Text == "Text");
            var mce = ce.AssertNodeMatches<TemplateElement>(2, t => t.Name == "mce");
            mce.AssertNodeMatches<TemplateText>(0, t => t.Text == "More");
        }

        [TestMethod]
        public void TestElementsWithNoChildren()
        {
            XmlParser parser = new XmlParser("<e/><me/>");

            Assert.IsNotNull(parser.Document);
            AssertDocumentAsNoErrors(parser.Document);
            //  should be two element, text and element
            Assert.AreEqual(2, parser.Document.Children.Count());

            parser.Document.AssertNodeMatches<TemplateElement>(0, t => t.Name == "e");
            parser.Document.AssertNodeMatches<TemplateElement>(1, t => t.Name == "me");
        }

        [TestMethod]
        public void TestElementsWithNoChildrenWithAttributes()
        {
            XmlParser parser = new XmlParser("<e a=\"b\"/><me/>");

            Assert.IsNotNull(parser.Document);
            AssertDocumentAsNoErrors(parser.Document);
            //  should be two element, text and element
            Assert.AreEqual(2, parser.Document.Children.Count());

            var e = parser.Document.AssertNodeMatches<TemplateElement>(0, t => t.Name == "e");
            e.AssertNodeMatches<TemplateAttribute>(0, t => t.Name == "a" && t.Value == "b");
            parser.Document.AssertNodeMatches<TemplateElement>(1, t => t.Name == "me");
        }

        [TestMethod]
        public void ParseElementWithNameSpace()
        {
            XmlParser parser = new XmlParser("<n:e></n:e>");

            Assert.IsNotNull(parser.Document);
            AssertDocumentAsNoErrors(parser.Document);
            //  should be two element, text and element
            Assert.AreEqual(1, parser.Document.Children.Count());

            var e = parser.Document.AssertNodeMatches<TemplateElement>(0, t => t.Name == "n:e" & t.HasNamespace && t.LocalName == "e" && t.Namespace == "n");
        }



        private void AssertDocumentAsNoErrors(TemplateDocument document)
        {
            if(document.GetErrors().Any())
            {
                string errors = "";
                foreach(var node in document.GetErrors())
                {
                    errors += node.Error + "\r\n";
                }
                Assert.Fail("Document Has Errors: \r\n" + errors);
            }
        }
    }

    public static class TemplateNodeExtensions
    {

        public static T AssertNodeMatches<T>(this TemplateNode parent, int index, Func<T, bool> match)
            where T : TemplateNode
        {
            T node = parent.Children.Skip(index).Take(1).OfType<T>().FirstOrDefault();

            Assert.IsNotNull(node);
            Assert.IsTrue(match(node));

            return node;
        }
    }
}
